#include<iostream>
#include<unistd.h>
#include<cerrno>  
#include<string.h>
#include<string>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    //父进程需要以读写的方式打开管道文件，父进程和子进程之间进行相互通信,父进程打开的管道文件地址是会被子进程继承下来的
    //所以它们指向的是同一份管道文件
    //【一定要让不同的进程看到同一份资源】
    //1.创建管道
    int pipefd[2] = {0};//将其初始化为0
    int n = pipe(pipefd);//pipefd是一个输出型参数
    if(n<0)
    {
        std::cout<<"pipe error, "<<errno<<":"<<strerror(errno)<<std::endl;
    }
    //pipe函数创建管道文件，将数组pipefd初始化成3和4，并返回，因为0,1,2已被占用
    std::cout<<"pipefd[0]: "<<pipefd[0]<<std::endl;//3->读端
    std::cout<<"pipefd[1]: "<<pipefd[1]<<std::endl;//4->写端
    
    //2.创建子进程
    pid_t id = fork();
    assert(id!=-1);//正常应该应用判断，我这里就断言

    if(id==0) //子进程
    {
        //3.关闭不需要的fd，让父进程进行读取，让子进程进行写入
        //我们想让子进程进行写入，所以关闭的是pipefd[0]读,保留pipefd[1]写
        close(pipefd[0]);

        //4.开始通信--结合某种场景---子进程将数据写入管道
        const std::string namestr = "嗨！我是子进程";
        int cnt = 1;
        char buffer[1024];
        while(true)
        {
            //先向buffer字符缓冲区格式化写入数据
            snprintf(buffer,sizeof(buffer),"%s,计数器：%d,我的PID:%d\n",namestr.c_str(),cnt++,getpid());
            //然后再将缓冲区的数据写到管道里
            write(pipefd[1],buffer,strlen(buffer));
            sleep(1);
        }

        close(pipefd[1]);

        //子进程执行完退出
        exit(0);
    }

    //父进程
    //3.为实现单向通信，需要关闭不需要的文件描述符fd
    close(pipefd[1]);

    //4.开始通信--结合某种场景---父进程从管道中读取数据
    char buffer[1024]={0};
    while(true)
    {
        //父进程先把管道里的数据读出来，放到缓冲区中
        int n = read(pipefd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n]='\0';
            std::cout<<"我是父进程，子进程给我的消息是:"<<buffer<<std::endl;
        }
        else if(n==0)
        {
            std::cout<<"我是父进程，我读到文件结尾了"<<std::endl;
            break;
        }
        else
        {
            std::cout<<"我是父进程，我读异常了"<<std::endl;
        }
    }

    close(pipefd[0]);//关闭管道读端

    //父进程等待子进程
    waitpid(id,&status,0);
    std::cout<<"sig: "<<(status & 0x7) << std::endl;//如果子进程一直写，但是父进程读取一段时间后退出
    //OS就会杀掉子进程

    return 0;
}
